﻿/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A
 * copy of the license can be found at http://go.microsoft.com/fwlink/?LinkID=131993. If
 * you cannot locate the Microsoft Public License, please send an email to
 * mixon@microsoft.com. By using this source code in any fashion, you are agreeing to
 * be bound by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/

using System;
using System.Windows;

namespace Microsoft.Controls
{
    public class VirtualizingStackPanel : VirtualizingPanel
    {
        protected override Size MeasureOverride(Size availableSize)
        {
            var myWidth = 0d;
            var myHeight = 0d;
            var initialIndexWithinStackPanel = 0; // Index 0 always contains the focused element when it's not visible in the ListBox.

            var indexWithinOriginalList = this.DataGenerator.FirstVisibleItemIndex;
            var indexWithinStackPanel = initialIndexWithinStackPanel; 

            while (myHeight < availableSize.Height &&
                indexWithinOriginalList < this.DataGenerator.TotalItemsCount)
            {
                UIElement currentItemContainer = null;

                // Add another container as a child
                if (indexWithinStackPanel < this.Children.Count)
                {
                    currentItemContainer = this.Children[indexWithinStackPanel];
                    var dataItem = this.DataGenerator.GetDataItem(indexWithinOriginalList);
                    this.DataGenerator.PrepareContainerForItemOverride(currentItemContainer, dataItem);
                }
                else
                {
                    currentItemContainer = this.DataGenerator.GetContainerForItemOverride();
                    var dataItem = this.DataGenerator.GetDataItem(indexWithinOriginalList);
                    this.DataGenerator.PrepareContainerForItemOverride(currentItemContainer, dataItem);
                    this.Children.Add(currentItemContainer);
                }

                indexWithinStackPanel++;
                indexWithinOriginalList++;

                // Measure
                currentItemContainer.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
                myHeight += currentItemContainer.DesiredSize.Height;
                myWidth = Math.Max(currentItemContainer.DesiredSize.Width, myWidth);
            }

            // If there's a partial item being displayed, the viewport size of the ScrollBar should not account for it. 
            // If the viewport size included partial items, it would not be possible to scroll them into view.
            var lastFullyVisibleIndex = indexWithinStackPanel;
            if (myHeight > availableSize.Height)
            {
                lastFullyVisibleIndex--;
            }
            this.DataGenerator.FullyVisibleItemCount = lastFullyVisibleIndex - initialIndexWithinStackPanel; 

            // Set values used to adjust the horizontal scrollbar
            this.DataGenerator.VisibleHorizontalPixelCount = availableSize.Width;
            this.DataGenerator.TotalHorizontalPixelCount = myWidth;

            // Remove any containers that are not visible (either because they fall outside of the visible area or because not enough items exist
            // to fill the visible area).
            while (indexWithinStackPanel < this.Children.Count)
            {
                var container = this.Children[indexWithinStackPanel] as FrameworkElement;
                // It's better to clear the container before removing it because if it has subscribed events, it won't be garbaged collected
                // when removed from the tree.
                this.DataGenerator.ClearContainerForItemOverride(container, container.DataContext);
                this.Children.RemoveAt(indexWithinStackPanel);
            }

            return new Size(myWidth, myHeight);
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            var yPosition = 0d;
            var xPosition = -this.DataGenerator.FirstVisibleHorizontalPixel;
            foreach (var child in this.Children)
            {
                var childRect = new Rect(xPosition, yPosition, finalSize.Width, child.DesiredSize.Height);
                child.Arrange(childRect);
                yPosition += child.DesiredSize.Height;
            }

            return finalSize;
        }

        // Returns the container that wraps the data item passed as a parameter if we have one created.
        // Otherwise, it returns null.
        public UIElement GetContainerForItem(object dataItem)
        {
            foreach (UIElement currentContainer in this.Children)
            {
                var containerFrameworkElement = currentContainer as FrameworkElement;
                if (containerFrameworkElement != null && Object.Equals(containerFrameworkElement.DataContext, dataItem))
                {
                    return containerFrameworkElement;
                }
            }
            return null;
        }
    }
}
